package cn.rayland.library.utils;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;

import android.graphics.Path;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import cn.rayland.api.Cura;
import cn.rayland.app.yandktest.Machine;
import cn.rayland.app.yandktest.X3gStatus;
import cn.rayland.app.yandktest.gpx;
import cn.rayland.library.bean.SliceSetting;

/**
 * 
 * 格式转换工具类 Created by gw on 2016/3/26.
 */
public class ConvertUtils {
	private static final String TAG = ConvertUtils.class.getSimpleName();
	public static final String TMP_X3G = "tmpX3g";
	private static final String SLICE_URL = "http://ray-land.cn:4000/uploadWithQ";
	private static final int READ_TIME_OUT = 8 * 60 * 1000;
	private static final int CONN_TIME_OUT = 8 * 1000;
	private static final String CHARSET = "utf-8"; 
	private static Handler handler = new Handler();
	public static final int DOWNLOAD_STL= 0X01;
	public static final int UPLOAD_STL = 0X02;
	public static final int STL_TO_GCODE = 0X03;
	public static final int GCODE_TO_X3G = 0X04;
	public static volatile boolean cancelConvert = false;
	private static final int BUFFER_SIZE = 1024 * 8;
	/**
	 * 将Stl文件转化为Gcode文件
	 * 
	 * @param file
	 *            stl文件
	 * @param setting
	 *            切片设置
	 * @param callback
	 *            切片完成的回调
	 */
	public static void stlToGcodeByCloud(final File file, final SliceSetting setting,
			final Callback callback) {
		new Thread(new Runnable() {

			public void run() {
				if (file == null)
					return;
				Log.d(TAG, "start upload " + file.getAbsolutePath());
				cancelConvert = false;
				if (callback != null) {
					handler.post(new Runnable() {
						@Override
						public void run() {
							callback.onPreConvert();
						}
					});
				}
				String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
				String PREFIX = "--", LINE_END = "\r\n";
				String CONTENT_TYPE = "multipart/form-data"; // 内容类型

				try {
					URL url = null;
					if (setting == null) {
						url = new URL(SLICE_URL);
					} else {
						url = new URL(SLICE_URL + setting.toParams());
					}
					FileInputStream is = new FileInputStream(file);
					StringBuffer sb = new StringBuffer();
					sb.append(PREFIX);
					sb.append(BOUNDARY);
					sb.append(LINE_END);
					sb.append("Content-Disposition: form-data; name=\"upload\"; filename=\""
							+ file.getName() + "\"" + LINE_END);
					sb.append("Content-Type: application/octet-stream; charset="
							+ CHARSET + LINE_END);
					sb.append(LINE_END);
					byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END)
							.getBytes();
					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setReadTimeout(READ_TIME_OUT);
					conn.setConnectTimeout(CONN_TIME_OUT);
					conn.setChunkedStreamingMode(BUFFER_SIZE);
					conn.setDoInput(true); // 允许输入流
					conn.setDoOutput(true); // 允许输出流
					conn.setUseCaches(false); // 不允许使用缓存
					conn.setRequestMethod("POST"); // 请求方式
					conn.setRequestProperty("Charset", CHARSET); // 设置编码
					conn.setRequestProperty("Connection", "Keep-Alive");
					conn.setRequestProperty("Content-Type", CONTENT_TYPE
							+ ";boundary=" + BOUNDARY);

					// 当文件不为空，把文件包装并且上传
					DataOutputStream dos = new DataOutputStream(conn
							.getOutputStream());
					
					dos.write(sb.toString().getBytes());
					byte[] bytes = new byte[BUFFER_SIZE];
					int lastProgress = 0;
					int len = -1;
					while ((len = is.read(bytes)) != -1 && !cancelConvert) {
						dos.write(bytes, 0, len);
						final int progress = is.getChannel().size() > 0 ? (int) ((1 - (is
								.available() * 1.0f / is.getChannel().size())) * 100)
								: -1;
						if (progress != lastProgress) {
							Log.d(TAG, "upload  " + progress + "%");
							if (callback != null) {
								handler.post(new Runnable() {
									@Override
									public void run() {
										callback.onConvertProgress(UPLOAD_STL,
												progress);
									}
								});
							}
						}
						lastProgress = progress;
					}
					is.close();
					dos.write(LINE_END.getBytes());
					dos.write(end_data);
					dos.flush();
					dos.close();

				if (conn.getResponseCode() == 200) {
						InputStream inputStream = conn.getInputStream();
						int fileLength = conn.getContentLength();
						Log.d(TAG, "fileLength = " + fileLength);
						int downloadLength = 0;

						File dir = new File(PathUtils.getSliceFolder());
						if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
							dir.mkdirs();
						}
						final File downloadFile = new File(PathUtils
								.getSliceFolder(), file.getName().replace(
								".stl", ".gcode"));
						FileOutputStream fos = new FileOutputStream(
								downloadFile);
						BufferedOutputStream bos1 = new BufferedOutputStream(fos);
						byte[] buffer = new byte[BUFFER_SIZE];
						int length = -1;
						int last = 0;
						while ((length = inputStream.read(buffer)) != -1
								&& !cancelConvert) {
							bos1.write(buffer, 0, length);
							downloadLength += length;
							final int progress = (int) (fileLength > 0 ? (downloadLength * 1.0f / fileLength) * 100
									: -1);
							if (progress != last) {
								Log.d(TAG, "download  " + progress + "%");
								if (callback != null) {
									handler.post(new Runnable() {
										public void run() {
											callback.onConvertProgress(
													DOWNLOAD_STL, progress);
										}
									});
								}
							}
							last = progress;
						}
						if (callback != null) {
							handler.post(new Runnable() {
								@Override
								public void run() {
									callback.onConvertSuccess(downloadFile,
											null);
								}
							});
						}
						closeStream(is);
						closeStream(bos1);
						closeStream(fos);

					} else {
						InputStream inputStream = conn.getErrorStream();
						final StringBuilder stringBuilder = new StringBuilder();
						int i;
						while ((i = inputStream.read()) != -1) {
							stringBuilder.append((char) i);
						}
						inputStream.close();
						if (callback != null) {
							handler.post(new Runnable() {
								@Override
								public void run() {
									callback.onConvertFailed(stringBuilder
											.toString());
								}
							});
						}

					}
					conn.disconnect();
				} catch (final Exception e) {
					e.printStackTrace();
					if (callback != null) {
						handler.post(new Runnable() {
							@Override
							public void run() {
								callback.onConvertFailed(e.getMessage());
							}
						});
					}
				}
			}
		}).start();
	}

	public static void stlToGcodeByLocal(final File file, SliceSetting setting, final Callback callback){
		new Thread(){
			@Override
			public void run() {
				final File gcodFile = new File(PathUtils.getSliceFolder(), file.getName().replace(".stl", ".gcode"));
				String fdmPrinter = Environment.getExternalStorageDirectory().getAbsolutePath()+"/fdmprinter.json";
				String[] command = {"CuraEngine","slice","-v","-j",fdmPrinter,"-o",gcodFile.getAbsolutePath(),"-e1","-s","infill_line_distance=0","-e0","-l",file.getAbsolutePath()};
				if (callback != null) {
					handler.post(new Runnable() {
						@Override
						public void run() {
							callback.onPreConvert();
						}
					});
				}
				try{
					Cura.slice(command);
				}catch(final Exception e){
					e.printStackTrace();
					if (callback != null) {
						handler.post(new Runnable() {
							@Override
							public void run() {
								callback.onConvertFailed(e.getMessage());
							}
						});
					}
				}
				if (callback != null) {
					handler.post(new Runnable() {
						@Override
						public void run() {
							callback.onConvertSuccess(gcodFile, null);
						}
					});
				}
			}
		}.start();
		
		
	}
	
	/**
	 * 将Gcode文件转化为X3g文件
	 * @param file
	 *            gcode文件
	 * @param machine
	 *            机器参数
	 * @param callback
	 *            转化完成的回调
	 * @return 生成的x3g文件路径
	 */
	public static void gcodeToX3g(final File file, final Machine machine,
			final Callback callback) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				cancelConvert = false;
				if(callback!=null){
					handler.post(new Runnable() {
						@Override
						public void run() {
							callback.onPreConvert();
						}
					});
				}
				FileReader fr = null;
				BufferedReader br = null;
				FileOutputStream fos = null;
				BufferedOutputStream bos = null;
				String bufferString = null;
				try {
					fr = new FileReader(file);
					br = new BufferedReader(fr);
					File dir = new File(PathUtils.getGpxFolder());
					if (!dir.exists() && dir.isDirectory()) {
						dir.mkdirs();
					}
					final File x3gFile = new File(PathUtils.getGpxFolder(), TMP_X3G);
					fos = new FileOutputStream(x3gFile);
					bos = new BufferedOutputStream(fos);
					
					final X3gStatus x3gStatus = new X3gStatus(0, 0);
					int lastProgress = 0;
					long fileLength = file.length();
					while((bufferString = br.readLine()) != null && !cancelConvert){
						if(bufferString.trim().length()>0){
							bos.write(gpx.gpxConverPieceGcode(bufferString.trim(), x3gStatus));
							final int progress = (int) ((x3gFile.length()*1.0f/fileLength) * 100);
							if(progress != lastProgress){
								Log.d(TAG, "gcode to x3g  "+progress + "%");
								if(callback!=null){
									handler.post(new Runnable() {
										@Override
										public void run() {
											callback.onConvertProgress(GCODE_TO_X3G, progress);
										}
									});
								}
							}
							lastProgress = progress;
						}
					}
					handler.post(new Runnable() {

						@Override
						public void run() {
							if (callback != null) {
								callback.onConvertSuccess(x3gFile, x3gStatus);
							}
						}
					});
					
				} catch (Exception e) {
					Log.e(TAG, "ERROR GCODE: "+bufferString);
					handler.post(new Runnable() {

						@Override
						public void run() {
							if(callback!=null){
								callback.onConvertFailed("convert failed");
							}
						}
					});
				}finally{
					closeStream(br);
					closeStream(fr);
					closeStream(bos);
					closeStream(fos);
				}
			}
		}).start();
		
	}

	/**
	 * 将Gcode字符串转化为X3g文件
	 * @param file
	 *            gcode文件
	 * @param machine
	 *            机器参数
	 * @param callback
	 *            转化完成的回调
	 * @return 生成的x3g文件路径
	 */
	public static void gcodeToX3g(final String gcodeStr, final Machine machine,
			final Callback callback) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					cancelConvert = false;
					if(callback!=null){
						handler.post(new Runnable() {
							@Override
							public void run() {
								callback.onPreConvert();
							}
						});
					}
					final X3gStatus x3gStatus = new X3gStatus(0, 0);
					byte[] x3gBytes = gpx.gpxConverPieceGcode(gcodeStr,
							x3gStatus);
					final File x3gFile = save2File(x3gBytes, PathUtils.getGpxFolder(), TMP_X3G);
					handler.post(new Runnable() {

						@Override
						public void run() {
							if (callback != null) {
								callback.onConvertSuccess(x3gFile, x3gStatus);
							}
						}
					});
					
				} catch (Exception e) {
					handler.post(new Runnable() {

						@Override
						public void run() {
							if(callback!=null){
								callback.onConvertFailed("convert failed");
							}
						}
					});
				}
			}
		}).start();
		
	}
	

	/**
	 * 根据InputStream生成文件
	 */
	public static File save2File(InputStream is, String filePath,
			String fileName) {
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		File file = null;
		try {
			File dir = new File(filePath);
			if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
				dir.mkdirs();
			}
			file = new File(filePath, fileName);
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			byte[] buffer = new byte[BUFFER_SIZE];
			int length = 0;
			while ((length = is.read(buffer)) != -1) {
				bos.write(buffer, 0, length);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeStream(is);
			closeStream(bos);
			closeStream(fos);
		}
		return file;
	}
	
	/**
	 * 根据bytes[]生成文件
	 */
	public static File save2File(byte[] bytes, String filePath,
			String fileName) {
		BufferedOutputStream bos = null;
		FileOutputStream fos = null;
		File file = null;
		try {
			File dir = new File(filePath);
			if (!dir.exists() && dir.isDirectory()) {// 判断文件目录是否存在
				dir.mkdirs();
			}
			file = new File(filePath, fileName);
			fos = new FileOutputStream(file);
			bos = new BufferedOutputStream(fos);
			bos.write(bytes);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeStream(bos);
			closeStream(fos);
		}
		return file;
	}
	
	public static void cancelConvert(){
		cancelConvert = true;
	}
	
	public interface ConvertCallback{
    	void onPreConvert(int type);
    	void onConvertProgress(int type, int progress);
    	void onConvertSuccess(int type, X3gStatus x3gStatus);
    	void onConvertFailed(int type, String error);
    }
	
	protected interface Callback {
		void onPreConvert();
		void onConvertSuccess(File file, X3gStatus consum);
		void onConvertProgress(int type, int progress);	
		void onConvertFailed(String error);
	}
	
	
	private static <T extends Closeable> void closeStream(T stream){
		if(stream != null){
			try {
				stream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}